%{
    #include <stdio.h>
    #include "Hashmap.h"
    #include "Quads.h"

    extern Hashmap *gMap;
    extern Hashmap *map;
    int label=0;

    Quad sortArgs(Quad act, struct strvaleur *fct){
        Quad q = NULL;
        Quad trav;
        int output = 0;
        fct->Specific.nbParams[0]=0;
        fct->Specific.nbParams[1]=0;
        fct->Specific.nbParams[2]=0;
        for(trav=act; trav!=NULL; trav=trav->next){
           if(trav->op=='o'){
                if(output!=0){
                    printf("Plusieurs output ont été définie\n");
                    exit(0);
                }
                Quad new;
                fct->type=trav->res->type;
                new = quad_gen('o', NULL, NULL, trav->res);
                fct->Specific.nbParams[1]++;
                addQuadList(&q, new);
                output++;
           }
        }
        for(trav=act; trav!=NULL; trav=trav->next){
           if(trav->op=='i'){
                Quad new;
                new = quad_gen('i', NULL, NULL, trav->res);
                addQuadList(&q, new);
                fct->Specific.nbParams[0]++;
           }
        }
        for(trav=act; trav!=NULL; trav=trav->next){
           if(trav->op=='l'){
                Quad new;
                new = quad_gen('l', NULL, NULL, trav->res);
                addQuadList(&q, new);
                fct->Specific.nbParams[2]++;
           }
        }
        for(trav=act; trav!=NULL; trav=trav->next){
           if(trav->op=='g'){
                Quad new;
                new = quad_gen('g', NULL, NULL, trav->res);
                addQuadList(&q, new);
           }
        }
        //freeQuadList(act);
        return q;
    }
%}

%union{
    struct strvaleur* v;
    union value
    {
        int integer;
        float flottant;
        char* string;
    }Value;
    struct {
        struct strvaleur* addr;
        struct strquad* code;
    } codegen;
};

%token beginDoc
%token endDoc

%token <v> start
%token end
%token tinput
%token toutput
%token argin
%token constant
%token global
%token local
%token blank
%token emptyset
%token affectation
%token mult
%token in
%token delimiter
%token repeat
%token print
%token scan

%token <Value.integer> type

%token tif
%token eif
%token twhile
%token tfor

%token <Value.string> ident
%token <Value.integer> entier
%token <Value.integer> boolean

%token <Value.integer> logicop
%token kwto
%token mbox

%type <codegen> ASSIGNEMENT
%type <codegen> CONDITION
%type <codegen> VALUE
%type <v> IDENT
%type <codegen> EXPR
%type <codegen> MBOX
%type <codegen> BODY
%type <codegen> INSTRUCTION
%type <codegen> FUNCTION
%type <codegen> HEADER
%type <codegen> CODE
%type <codegen> ARGS
%type <codegen> IF
%type <codegen> EIF
%type <codegen> WHILE
%type <codegen> FOR
%type <codegen> INPUT
%type <codegen> INPUTARG
%type <codegen> OUTPUT
%type <codegen> OUTPUTARG
%type <codegen> GLOBAL
%type <codegen> CONSTANT
%type <codegen> LOCAL
%type <codegen> LOCALARG
%type <codegen> ARG
%type <codegen> CSTARG
%type <codegen> MULTARG
%type <Value.integer> TYPE

%left '-' '+'
%left logicop
%left mult 

%start HEADER

%%

HEADER : beginDoc CODE endDoc       { 
                                       printf("\nComme dirait l'autre : Match !\n");

                                       Valeur* v = searchInTable(gMap, "main");
                                       if(v==NULL || !v->Value.isFct){
                                            printf("La fonction main n'a pas été définie\n");
                                            YYERROR;
                                       }
                                       Quad q;
                                       q = quad_gen('m', NULL, NULL, v);
                                       $$.code = q;
                                       q = quad_gen('b', NULL, NULL, v);
                                       addQuadList(&$$.code, q);
                                       q = quad_gen('x', NULL, NULL, v);
                                       addQuadList(&$$.code, q);
                                       addQuadList(&$$.code, $2.code);

                                       quad_print($$.code);
                                       mipser($$.code);
                                    }
       ;

CODE : FUNCTION         { $$ = $1; }
     | CODE FUNCTION    { 
                            $$ = $1;
                            addQuadList(&$$.code, $2.code); 
                        }
     ;

FUNCTION : start ARGS BODY end      {                                       
                                        Quad q;
                                        q = quad_gen('f', NULL, NULL, $1); 
                                        $1->Memory.ptr=$3.code;
                                        $$.code = q;
                                        //on réordonne nos args
                                        $2.code = sortArgs($2.code, $1);
                                        addQuadList(&$$.code, $2.code); 
                                        addQuadList(&$$.code, $3.code); 

                                        q = quad_gen('e', NULL, NULL, $1); 
                                        addQuadList(&$$.code, q); 
                                    }
         ;

ARGS : /* pas d'argument */         { $$.code = NULL;}
     | INPUT ARGS                   { $$.code = $1.code;
                                         addQuadList(&$$.code, $2.code);
                                    }
     | OUTPUT ARGS                  { $$.code = $1.code;
                                         addQuadList(&$$.code, $2.code);
                                    }
     | GLOBAL ARGS                  { $$.code = $1.code;
                                         addQuadList(&$$.code, $2.code);
                                    }
     | LOCAL ARGS                   { $$.code = $1.code;
                                         addQuadList(&$$.code, $2.code);
                                    }
     | CONSTANT ARGS                { $$.code = $1.code;
                                         addQuadList(&$$.code, $2.code);
                                    }
     ;

IF : tif '{' CONDITION '}' '{' BODY '}' {
                                            if($3.addr->type!=BOOL){
                                                printf("Condition sur un non-booléen");
                                                YYERROR;
                                            }
                                            $$ = $3;
                                            Quad q;
                                            q = quad_gen(CONDOP+CIF, $3.addr, tmpInt(label) , NULL);
                                            addQuadList(&$$.code, q);
                                            addQuadList(&$$.code, $6.code);
                                            q = quad_gen(CONDOP+CFI, tmpInt(label), NULL, NULL);
                                            addQuadList(&$$.code, q);

                                            label++;
                                        }

   ;

EIF : eif '{' CONDITION '}' '{' BODY '}' '{' BODY '}' {
                                                          if($3.addr->type!=BOOL){
                                                              printf("Condition sur un non-booléen");
                                                              YYERROR;
                                                          }
                                                          $$ = $3;
                                                          Quad q;
                                                          q = quad_gen(CONDOP+CEIF, $3.addr, tmpInt(label) , NULL);
                                                          addQuadList(&$$.code, q);
                                                          addQuadList(&$$.code, $6.code);
                                                          q = quad_gen(CONDOP+CELSE, tmpInt(label), NULL, NULL);
                                                          addQuadList(&$$.code, q);
                                                          addQuadList(&$$.code, $9.code);
                                                          q = quad_gen(CONDOP+CFI, tmpInt(label), NULL, NULL);
                                                          addQuadList(&$$.code, q);

                                                          label++;
                                                      }

    ;

WHILE : twhile '{' CONDITION '}' '{' BODY '}'     {
                                                      if($3.addr->type!=BOOL){
                                                          printf("Condition sur un non-booléen");
                                                          YYERROR;
                                                      }
                                                      Quad q;
                                                      q = quad_gen(CONDOP+CWHILE, tmpInt(label), NULL, NULL);
                                                      $$.code = q;

                                                      addQuadList(&$$.code, $3.code);

                                                      q = quad_gen(CONDOP+CDO, $3.addr, tmpInt(label) , NULL);
                                                      addQuadList(&$$.code, q);

                                                      addQuadList(&$$.code, $6.code);

                                                      q = quad_gen(CONDOP + CENDWHILE, tmpInt(label), NULL, NULL);
                                                      addQuadList(&$$.code, q);

                                                      label++;
                                                  }
      ;

FOR : tfor '{' '$' ASSIGNEMENT '$'  kwto '$' EXPR '$' '}' '{' BODY '}' {
                                                      $$ = $4; // On empile l'assignement en premier

                                                      Quad q;

                                                      q = quad_gen(CONDOP+CFOR, $8.addr, tmpInt(label), $4.code->res);
                                                      addQuadList(&$$.code,q);

                                                      addQuadList(&$$.code, $12.code);

                                                      q = quad_gen(CONDOP + CENDFOR, tmpInt(label), NULL, NULL);
                                                      addQuadList(&$$.code, q);

                                                      label++;
                                                  }
    | tfor '{' IDENT kwto '$' EXPR '$' '}' '{' BODY '}' {

                                                      Quad q;

                                                      q = quad_gen(CONDOP+CFOR, $6.addr, tmpInt(label), $3);

                                                      $$.code = q; // On empile le FOR en premier

                                                      addQuadList(&$$.code, $10.code);

                                                      q = quad_gen(CONDOP + CENDFOR, tmpInt(label), NULL, NULL);
                                                      addQuadList(&$$.code, q);

                                                      label++;
                                                  }
    ;

CONDITION : '$' EXPR '$' {
                            if($2.addr->type!=BOOL){
                                Quad q; 
                                $$.addr = tmpBool(0);
                                $$.addr->Specific.isConstant=0;
                                q = quad_gen(BOOLOP+POS, $2.addr, NULL, $$.addr);
                                $$.code = $2.code;
                                addQuadList(&$$.code, q);
                            }else{
                                $$=$2;
                            }
                        }
           ;

INPUT : tinput '{' '$' emptyset '$' '}'     {}
      | tinput '{' '$' INPUTARG '$' '}'          { $$ = $4; }
      ;

OUTPUT : toutput '{' '$' emptyset '$' '}'   {}
       | toutput '{' '$' OUTPUTARG '$' '}'   { $$ = $4; }
       ;

GLOBAL : global '{' '$' emptyset '$' '}'    {}
       | global '{' '$' ARG '$' '}'         { $$ = $4; }
       ;

LOCAL : local '{' '$' emptyset '$' '}'      {}
      | local '{' '$' LOCALARG '$' '}'      { $$ = $4; }
      ;

CONSTANT : constant '{' '$' emptyset '$' '}'   {}
         | constant '{' '$' CSTARG '$' '}'     { $$ = $4; }
         ;

INPUTARG : ARG          {
                            Quad q;
                            q = quad_gen('i', NULL, NULL, $1.addr);
                            $$.code = q;
                        }
         | ARG ',' INPUTARG
                        {
                            Quad q;
                            q = quad_gen('i', NULL, NULL, $1.addr);
                            $$.code = q;
                            addQuadList(&$$.code, $3.code);
                        }
         ;

OUTPUTARG : ARG         {
                            Quad q;
                            q = quad_gen('o', NULL, NULL, $1.addr);
                            $$.code = q;
                        }
/*         | ARG ',' OUTPUTARG
                        {
                            Quad q;
                            q = quad_gen('o', NULL, NULL, $1.addr);
                            $$.code = q;
                            addQuadList(&$$.code, $3.code);
                        }*/
         ;

ARG : ident in TYPE     {//ajout à la tds
                                        Valeur* val = addInTable(map, $1);
                                        if(val==NULL){
                                            printf("\nRedefinition du symbole %s\n", $1);
                                            YYERROR;
                                        }
                                        val->Specific.isConstant=0;
                                        val->type = $3;
                                        $$.addr = val;
                                        $$.code = NULL;
                                }
       ;

LOCALARG : ARG          {
                            Quad q;
                            q = quad_gen('l', NULL, NULL, $1.addr);
                            $$.code = q;
                        }
         | ARG ',' LOCALARG
                        {
                            Quad q;
                            q = quad_gen('l', NULL, NULL, $1.addr);
                            $$.code = q;
                            addQuadList(&$$.code, $3.code);
                        }
         ;

CSTARG   : ident '=' VALUE in TYPE      {
                    if($3.addr->type==$5){
                        Valeur* val = addInTable(map, $1);
                        if(val==NULL){
                            printf("\nRedefinition du symbole %s\n", $1);
                            YYERROR;
                        }
                        //donner la valeur
                        val->type = $3.addr->type;
                        val->Specific.isConstant=1;
                    }else{
                        YYERROR;
                    }
                }
         | ident '=' VALUE in TYPE ',' CSTARG {
                    if($3.addr->type==$5){
                        Valeur* val = addInTable(map, $1);
                        if(val==NULL){
                            printf("\nRedefinition du symbole %s\n", $1);
                            YYERROR;
                        }
                        //donner la valeur
                        val->type = $3.addr->type;
                        val->Specific.isConstant=1;
                    }else{
                        YYERROR;
                    }
                }
         ;

TYPE : type                     { $$ = $1; }
     | type VECTOR              {}
     ;

VECTOR : '^' '{' entier '}'         { printf("vector "); }
       ;

BODY : /** vide **/                 { $$.code = NULL; }
     | INSTRUCTION                  { 
                                        $$ = $1; 
                                    }
     | INSTRUCTION delimiter BODY   { 
                                        $$ = $1; 
                                        addQuadList(&$$.code, $3.code);
                                    }
     ;

INSTRUCTION  : '$' ASSIGNEMENT '$'  { $$ = $2; }
             | WHILE                { $$ = $1; }
             | IF                   { $$ = $1; }
             | EIF                  { $$ = $1; }
             | FOR                  { $$ = $1; }
             | LOCALARG             {}
             | '$' print '{' EXPR '}' '$'  {
                                        Quad q;
                                        q = quad_gen('p',$4.addr,NULL,NULL);
                                        $$.code = $4.code;
                                        addQuadList(&$$.code, q);
                                    }
             | '$' print '$'  {
                                        Quad q;
                                        q = quad_gen('p',NULL,NULL,NULL);
                                        addQuadList(&$$.code, q);
                                    }
             | '$' EXPR '$'         { 
                                      Quad q;
                                      q = quad_gen('=',$2.addr,NULL,NULL); // Affectation vide pour depilement de resultat schnitzelien
                                      $$.code = $2.code;
                                      addQuadList(&$$.code, q);
                                    }
             ;

MBOX : mbox '{' ident '(' '$' MULTARG '$' ')' '}'   {
                                                Valeur* v = searchInTable(gMap, $3);
                                                if(v==NULL || !v->Value.isFct){
                                                   printf("La fonction %s n'a pas été définie\n", $3);
                                                   free($3);
                                                   YYERROR;
                                                }
                                                Quad q;
                                                q = quad_gen('m', NULL, NULL, v);
                                                $$.code = q;
                                                addQuadList(&$$.code, $6.code);
                                                q = quad_gen('b', NULL, NULL, v);
                                                addQuadList(&$$.code, q);
                                            }
     | mbox '{' ident '(' ')' '}'           {
                                                Valeur* v =searchInTable(gMap, $3);
                                                if(v==NULL || !v->Value.isFct){
                                                   printf("La fonction %s n'a pas été définie\n", $3);
                                                   free($3);
                                                   YYERROR;
                                                }
                                                Quad q;
                                                q = quad_gen('m', NULL, NULL, v);
                                                $$.code = q;
                                                q = quad_gen('b', NULL, NULL, v);
                                                addQuadList(&$$.code, q);
                                            }
     ;

MULTARG : EXPR              {
                                Quad q;
                                q = quad_gen('a', NULL, NULL, $1.addr);
                                $$.code = $1.code;
                                addQuadList(&$$.code, q);
                            }
        | EXPR ',' MULTARG  {
                                Quad q;
                                q = quad_gen('a', NULL, NULL, $1.addr);
                                $$.code = $1.code;
                                addQuadList(&$$.code, q);
                                addQuadList(&$$.code, $3.code);
                            }
        ;

ASSIGNEMENT : IDENT affectation EXPR    { 
                                            if($3.addr->type==$1->type){
                                                Quad q;
                                                q = quad_gen('=',$3.addr,NULL, $1);
                                                $$.code = $3.code;
                                                addQuadList(&$$.code, q);
                                            }else{
                                                printf("TYPE : %d\n", $3.addr->type);
                                                printf("\nValeur et type de variable différents\n");
                                                YYERROR;
                                            }
                                        }
            ;

EXPR : EXPR '-' EXPR   {
                            if($1.addr->type==BOOL || $3.addr->type==BOOL){
                                printf("\nAddition impossible sur des booléens\n");
                                YYERROR;
                            }
                            Quad q; 
                            $$.addr = tmpInt(0);
                            $$.addr->Specific.isConstant=0;
                            q = quad_gen('-', $1.addr, $3.addr, $$.addr);
                            $$.code = $1.code;
                            addQuadList(&$$.code, $3.code);
                            addQuadList(&$$.code, q);

                        }
     | EXPR '+' EXPR   {
                            if($1.addr->type==BOOL || $3.addr->type==BOOL){
                                printf("\nAddition impossible sur des booléens\n");
                                YYERROR;
                            }
                            Quad q; 
                            $$.addr = tmpInt(0);
                            $$.addr->Specific.isConstant=0;
                            q = quad_gen('+', $1.addr, $3.addr, $$.addr);
                            $$.code = $1.code;
                            addQuadList(&$$.code, $3.code);
                            addQuadList(&$$.code, q);

                        }
     | EXPR mult EXPR  { 
                            if($1.addr->type==BOOL || $3.addr->type==BOOL){
                                printf("\nAddition impossible sur des booléens\n");
                                YYERROR;
                            }
                            Quad q; 
                            $$.addr = tmpInt(0);
                            $$.addr->Specific.isConstant=0;
                            q = quad_gen('*', $1.addr, $3.addr, $$.addr);
                            $$.code = $1.code;
                            addQuadList(&$$.code, $3.code);
                            addQuadList(&$$.code, q);

                        }
     | EXPR logicop EXPR {
                            if(!($2==VEE||$2==WEDGE||$2==NEQ||$2==EQ||$2==LEQ||$2==GEQ||$2==LT||$2==GT)){
                                printf("Operateur invalide");
                                YYERROR;
                            }
                            Quad q; 
                            $$.addr = tmpBool(0);
                            $$.addr->Specific.isConstant=0;
                            q = quad_gen(BOOLOP+$2, $1.addr, $3.addr, $$.addr);
                            $$.code = $1.code;
                            addQuadList(&$$.code, $3.code);
                            addQuadList(&$$.code, q);
                        }
     | logicop EXPR     {
                            if($1!=NEG){
                                printf("Operateur invalide");
                                YYERROR;
                            }
                            Quad q; 
                            $$.addr = tmpBool(0);
                            $$.addr->Specific.isConstant=0;
                            q = quad_gen(BOOLOP+$1, $2.addr, NULL, $$.addr);
                            $$.code = $2.code;
                            addQuadList(&$$.code, q);
                        }
     | '(' EXPR ')'     { 
                            $$ = $2;
                        }
     | scan             {
                            $$.addr = tmpInt(0);
                            $$.addr->Specific.isConstant=0;
                            Quad q;
                            q = quad_gen('s',NULL,NULL,$$.addr);
                            $$.code = q;
                        }
     | VALUE            { 
                            $$ = $1;
                        }
     | '-' VALUE            {
                            if($2.addr->type==BOOL){
                                printf("\nMoins unaire impossible sur des booléens\n");
                                YYERROR;
                            }

                            Quad q; 
                            $$.addr = tmpInt(0);
                            $$.addr->Specific.isConstant=0;
                            q = quad_gen('-', tmpInt(0), $2.addr, $$.addr);
                            $$.code = $2.code;
                            addQuadList(&$$.code, q);
                        }
     ;

VALUE : IDENT           { $$.addr = $1; $$.code = NULL; }
      | entier          { $$.addr = tmpInt($1); $$.code = NULL; }
      | boolean         { $$.addr = tmpBool($1); $$.code = NULL; }
      | MBOX            { 
                            Quad q; 
                            $$.addr = tmpInt(0);
                            $$.addr->Specific.isConstant = 0;
                            $$.code = $1.code;
                        }
      ;

IDENT : ident '_' '{' entier '}'        { //chopper la valeur dans le tableau
                                        }
      | ident '_' '{' IDENT '}'         {
                                            if($<v>4->type==ENTIER){
                                            }else{
                                                printf("\nL'indice du tableau n'est pas un entier\n");
                                                YYERROR;
                                            }
                                                //adresse tableau
                                        }
      | ident                           {
                                            $$=searchInTable(map, $1);
                                            if($$==NULL){
                                               printf("La variable %s n'a pas été définie\n", $1);
                                               free($1);
                                               YYERROR;
                                            }
                                            free($1);
                                        }
      ;

%%

extern int yylex();
extern int yyparse();
extern FILE *yyin;

int yyerror ( char* s )
{
    printf("Une erreur s'est produite : %s\n", s);
    return 0;
}

int main(int argc, char** argv){

    map=newScopeTable(NULL,0);

    FILE *file = fopen(argv[1], "r");
    if(!file){
       return 1; 
    }
    yyin = file;
    yyparse();
    freeTable(map);
    fclose(file);
    return 0;
}
